package Q17_14_Smallest_K; import java.util.Random; import CtCILibrary.AssortedMethods; public class QuestionD { public static class PartitionResult { int leftSize; int middleSize; public PartitionResult(int left, int middle) { this.leftSize = left; this.middleSize = middle; } } public static int[] smallestK(int[] array, int k) { if (k <= 0 || k > array.length) throw new IllegalArgumentException(); int threshold = rank(array, k - 1); int[] smallest = new int[k]; int count = 0; for (int a : array) { if (a < threshold) { smallest[count] = a; count++; } } while (count < k) { smallest[count] = threshold; count++; } return smallest; } /* Find value with rank k in array. */ public static int rank(int[] array, int k) { if (k >= array.length) { throw new IllegalArgumentException(); } return rank(array, k, 0, array.length - 1); } /* Find value with rank k in sub array between start and end. */ private static int rank(int[] array, int k, int start, int end) { /* Partition array around an arbitrary pivot. */ int pivot = array[randomIntInRange(start, end)]; PartitionResult partition = partition(array, start, end, pivot); int leftSize = partition.leftSize; int middleSize = partition.middleSize; if (k < leftSize) { // Rank k is on left half return rank(array, k, start, start + leftSize - 1); } else if (k < leftSize + middleSize) { // Rank k is in middle return pivot; // middle is all pivot values } else { // Rank k is on right return rank(array, k - leftSize - middleSize, start + leftSize + middleSize, end); } } /* Partition result into < pivot, equal to pivot -> bigger than pivot. */ private static PartitionResult partition(int[] array, int start, int end, int pivot) { int left = start; /* Stays at (right) edge of left side. */ int right = end; /* Stays at (left) edge of right side. */ int middle = start; /* Stays at (right) edge of middle. */ while (middle <= right) { if (array[middle] < pivot) { /* Middle is smaller than the pivot. Left is either * smaller or equal to the pivot. Either way, swap * them. Then middle and left should move by one. */ swap(array, middle, left); middle++; left++; } else if (array[middle] > pivot) { /* Middle is bigger than the pivot. Right could have * any value. Swap them, then we know that the new * right is bigger than the pivot. Move right by one. */ swap(array, middle, right); right--; } else if (array[middle] == pivot) { /* Middle is equal to the pivot. Move by one. */ middle++; } } /* Return sizes of left and middle. */ return new PartitionResult(left - start, right - left + 1); } public static int randomIntInRange(int min, int max) { Random rand = new Random(); return rand.nextInt(max + 1 - min) + min; } /* Swap values at index i and j. */ public static void swap(int[] array, int i, int j) { int t = array[i]; array[i] = array[j]; array[j] = t; } public static void main(String[] args) { int[] array = {1, 5, 2, 3, 2, 9, -1, 11, 6, 13, 15, 2}; int[] smallest = smallestK(array, 6); System.out.println(AssortedMethods.arrayToString(smallest)); } }